package net.sureon.common.mail;

import java.util.Locale;

import net.sureon.app.AppContext;
import net.sureon.app.UserSession;
import net.sureon.common.config.SureonConfig;
import net.sureon.common.exception.SureonException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class helps in sending out mail asynchronously. It processes mails by
 * going through the recipient lists and then for each recipient create a
 * customized mail based on a template and then send out the mail, all this is
 * done by one thread. It uses a reusable thread pool, the size of the thread
 * pool is configurable in the "mail" config category using the
 * "mail.thread.count" attribute.
 * 
 */
public class MailProcessor {
	private static final Log LOGGER = LogFactory.getLog(MailProcessor.class);

	private static final int DEFAULT_THREAD_COUNT = 2;
	private transient MailThreadPool threadPool;
	private static MailProcessor instance = null;

	/**
	 * Returns the singleton instance of the this class
	 * 
	 * @return The MailProcessor instance
	 */
	public static MailProcessor getInstance() throws SureonException {
		if (instance == null) {
			instance = new MailProcessor();
		}
		return instance;
	}

	private MailProcessor() {

		int threadCount = DEFAULT_THREAD_COUNT;

		int threadCountValue = SureonConfig.getInstance().getMailConfig()
				.getThreadPoolSize();
		if (threadCountValue != 0) {
			threadCount = Integer.valueOf(threadCountValue);
		}
		threadPool = new MailThreadPool(threadCount);
	}

	/**
	 * Use this method to submit a request to send emails using a specific
	 * template. It will create as well as send emails asynchronously.
	 * 
	 * @param key
	 *            The Key that identiies the task
	 * @param data
	 *            The MailData that has all the relevant data to send the mail.
	 * @param templateName
	 *            The Template Name
	 * @param locale
	 *            The Locale of the email, this Locale will apply to all the
	 *            emails. If this Locale is null then the site's default locale
	 *            will be used
	 * @param creator
	 *            An instance of MailCreator interface which will be used create
	 *            the mail
	 * @param listener
	 *            An instance of the MailListener
	 */
	public void processMail(Object key, MailData data, String templateName,
			Locale locale, MailCreator creator, MailListener listener)
			throws SureonException {
		// Create the Unit

		MailProcessingUnit unit = new MailProcessingUnit(data, templateName,
				key, locale, creator, listener, AppContext
						.getCurrentUserSession());
		// Assign it to the thread pool
		threadPool.assignUnit(unit);
	}

	/**
	 * This method avoids the creation of the MailCreator and MailListener
	 * instances, it uses the DefaultMailCreator and DefaultMailListener
	 * instead.
	 * 
	 * @param key
	 *            The Key that identiies the task
	 * @param data
	 *            The MailData that has all the relevant data to send the mail.
	 * @param templateName
	 *            The Template Name
	 */
	public void processMail(Object key, MailData data, String templateName,
			Locale locale) throws SureonException {

		DefaultMailCreator creator = new DefaultMailCreator();
		DefaultMailListener listener = new DefaultMailListener();
		MailProcessingUnit unit = new MailProcessingUnit(data, templateName,
				key, locale, creator, listener, AppContext
						.getCurrentUserSession());
		// Assign it to the thread pool
		threadPool.assignUnit(unit);

	}

	/**
	 * Inner class that is passed to the Thread pool to start processing the
	 * mail
	 */
	class MailProcessingUnit implements Runnable {
		private MailData mailData;
		private String templateName;
		private Object key;
		private Locale locale;
		private MailCreator creator;
		private MailListener listener;
		private UserSession sessionState;

		public MailProcessingUnit(MailData mailData, String templateName,
				Object key, Locale locale, MailCreator creator,
				MailListener listener, UserSession sessionState) {
			this.mailData = mailData;
			this.templateName = templateName;
			this.key = key;
			if(locale != null){
			    this.locale = locale;
			}else{
			    this.locale = AppContext.getLocale();
			}
			this.creator = creator;
			this.listener = listener;
			this.sessionState = sessionState;
		}

		public void run() {
			try {
				// Set the session state so that AppContext works in this thread
				AppContext.setCurrentUserSession(sessionState);
				// First inform Listen that the Mail Processing is starting
				listener.start(key, mailData.getFrom());
				// Init creator
				creator.preProcessMail(key, templateName, mailData, locale);
				// Create a sender
				MailSender sender = new MailSender();
				String[] to = mailData.getRecipients();
				for (int i = 0; i < to.length; i++) {
					// Create the mail
					String content = creator.createMail(key, to[i], mailData);
					try {
						// Now send the mail
						sender.send(mailData, to[i], content);
						listener.sendSuccessful(key, to[i]);
					} catch (SureonException exp) {
						listener.sendFailed(key, to[i], exp);
					}
				}
				// Clean up creator
				creator.postProcessMail(key);
				// In the end inform listener that Mail Processing has ended
				listener.done(key);
			} catch (Exception e) {
				LOGGER.error("The Mail Processing failed for key "
						+ key.toString(), e);
			}
		}
	}
}